package com.guli.sms.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.guli.common.constant.SecKillConstant;
import com.guli.common.pojo.vo.SecKillSessionAndRelationVo;
import com.guli.common.pojo.SeckillSkuRelationEntity;
import com.guli.common.pojo.vo.StatusCodeEnum;
import com.guli.common.utils.R;
import com.guli.mall.serviceBase.globalException.MallException;
import com.guli.sms.feign.SecKillFeignClient;
import com.guli.sms.service.SeckillSkuRelationService;
import com.guli.sms.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.guli.sms.mapper.SeckillSessionMapper;
import com.guli.common.pojo.SeckillSessionEntity;
import com.guli.sms.service.SeckillSessionService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;

@Slf4j
@Service("seckillSessionService")
public class SeckillSessionServiceImpl extends ServiceImpl<SeckillSessionMapper, SeckillSessionEntity> implements SeckillSessionService {

    @Resource
    SeckillSkuRelationService seckillSkuRelationService;

    @Resource
    SecKillFeignClient secKillFeignClient;

    @Override
    public Page<SeckillSessionEntity> queryPage(Integer currentPage, Integer size, Map<String, Object> params) {

        Page<SeckillSessionEntity> page = new Page<>(currentPage, size);

        return this.page(page);
    }


    @Override
    public List<SecKillSessionAndRelationVo> getLatestSecKillSkuByDateInterval(Integer days) {

        Date startDate = DateUtil.getTodayZeroHourZeroMinuteZeroSecond();

        Date endDate = DateUtil.timeAfterDays(startDate, days);

        QueryWrapper<SeckillSessionEntity> wrapper = new QueryWrapper<>();

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        /**
         * 不格式化的话，虽然也能用，但是日期范围不准确
         */
//        wrapper.between("start_time", startDate, endDate);

        wrapper.between("start_time", dateFormat.format(startDate), dateFormat.format(endDate));

        List<SeckillSessionEntity> sessionEntities = this.list(wrapper);

        List<SecKillSessionAndRelationVo> sessionAndRelationVos = sessionEntities.parallelStream().map(session -> {
            Long sessionId = session.getId();

            List<SeckillSkuRelationEntity> list = seckillSkuRelationService.getSeckillRelationsBySeckillSessionId(sessionId);

            SecKillSessionAndRelationVo vo = new SecKillSessionAndRelationVo();

            BeanUtils.copyProperties(session, vo);

            vo.setSeckillSkuRelationEntities(list);

            return vo;
        }).collect(Collectors.toList());

        return sessionAndRelationVos;
    }

    @Override
    public void saveBatchAndCheckIfNeedToRefreshRedisCache(List<SeckillSessionEntity> seckillSessions) {

        /**
         * 以事务的方式批量保存场次信息
         */
        SeckillSessionService o = (SeckillSessionService) AopContext.currentProxy();

        o.saveBatchWithTransactional(seckillSessions);

        checkIfNeedToRefreshRedis(seckillSessions);
    }

    /**
     * 检查要修改的秒杀场次信息是否已经上传到redis，如果已经上传，那么就刷新redis缓存
     * @param seckillSessions
     */
    private void checkIfNeedToRefreshRedis(List<SeckillSessionEntity> seckillSessions) {

        long latestSecKillDateTime = DateUtil.timeAfterDays(new Date(), SecKillConstant.PRE_UPLOAD_DAYS).getTime();

        long todayZeroTime = DateUtil.getTodayZeroHourZeroMinuteZeroSecond().getTime();

        // 要修改的场次信息已经在redis中的 session集合
        List<SeckillSessionEntity> sessionsAlreadyInRedis = seckillSessions.stream().filter(session -> {
            return session.getStartTime().getTime() > todayZeroTime &&
                    session.getStartTime().getTime() < latestSecKillDateTime;
        }).collect(Collectors.toList());


        if (CollectionUtils.isEmpty(sessionsAlreadyInRedis)) {
            return;
        }

        log.info("要修改（保存，修改，删除）的场次信息，已经上传到redis，现在刷新redis缓存，当前上传的秒杀场次信息是：{}", seckillSessions);

        R r = secKillFeignClient.refreshCache();

        if(!r.getCode().equals(StatusCodeEnum.SUCCESS.getCode())){
            throw new MallException(StatusCodeEnum.REFRESH_SEC_KILL_SPU_FAILED);
        }
    }

    @Transactional
    @Override
    public void saveBatchWithTransactional(List<SeckillSessionEntity> seckillSessions) {

        this.saveBatch(seckillSessions);

    }

    @Override
    public void updateBatchByIdAndCheckIfNeedToRefreshRedisCache(List<SeckillSessionEntity> seckillSessions) {
        SeckillSessionService o = (SeckillSessionService) AopContext.currentProxy();

        o.updateBatchByIdWithTransactional(seckillSessions);

        this.checkIfNeedToRefreshRedis(seckillSessions);
    }

    @Transactional
    @Override
    public void updateBatchByIdWithTransactional(List<SeckillSessionEntity> seckillSessions) {
        this.updateBatchById(seckillSessions);
    }


}